////////////////////////////////////////////////////////////////////////////////
/// @file    bsp_WM8978.c
/// @author  AE TEAM
/// @brief   THIS FILE PROVIDES ALL THE SYSTEM FUNCTIONS.
////////////////////////////////////////////////////////////////////////////////
/// @attention
///
/// THE EXISTING FIRMWARE IS ONLY FOR REFERENCE, WHICH IS DESIGNED TO PROVIDE
/// CUSTOMERS WITH CODING INFORMATION ABOUT THEIR PRODUCTS SO THEY CAN SAVE
/// TIME. THEREFORE, MINDMOTION SHALL NOT BE LIABLE FOR ANY DIRECT, INDIRECT OR
/// CONSEQUENTIAL DAMAGES ABOUT ANY CLAIMS ARISING OUT OF THE CONTENT OF SUCH
/// HARDWARE AND/OR THE USE OF THE CODING INFORMATION CONTAINED HEREIN IN
/// CONNECTION WITH PRODUCTS MADE BY CUSTOMERS.
///
/// <H2><CENTER>&COPY; COPYRIGHT MINDMOTION </CENTER></H2>
////////////////////////////////////////////////////////////////////////////////


// Define to prevent recursive inclusion
#define _BSP_WM8978_C_

// Files includes

#include "HAL_conf.h"
#include "HAL_device.h"
#include "bsp_WM8978.h"
#include "bsp_i2c_gpio.h"

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup MM32_Example_Layer
/// @{

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup BSP_WM8978
/// @{

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup BSP_WM8978_Exported_Constants
/// @{


static uint16_t wm8978_ReadReg(uint8_t _ucRegAddr);
static uint8_t wm8978_WriteReg(uint8_t _ucRegAddr, uint16_t _usValue);
static void I2S_GPIO_Config(void);
static void I2S_Mode_Config(SPI_I2S_STANDARD_TypeDef _usStandard, SPI_I2S_DATAFORMAT_TypeDef _usWordLen, SPI_I2S_AUDIO_FREQ_TypeDef _usAudioFreq, SPI_I2S_TRANS_MODE_TypeDef _usMode);
static void wm8978_CfgInOut(uint8_t _ucDacEn, uint8_t _ucAuxEn, uint8_t _ucLineEn, uint8_t _ucSpkEn, uint8_t _ucEarEn);
static void wm8978_CtrlGPIO1(uint8_t _ucValue);
static void wm8978_Reset(void);
static void wm8978_CfgAdc(uint8_t _ucMicEn, uint8_t _ucAuxEn, uint8_t _ucLineEn);
const u16 i2splaybuf[2] = {0X0000, 0X0000};


u8* i2srecbuf1;
u8* i2srecbuf2;



static uint16_t wm8978_RegCash[] = {
    0x000, 0x000, 0x000, 0x000, 0x050, 0x000, 0x140, 0x000,
    0x000, 0x000, 0x000, 0x0FF, 0x0FF, 0x000, 0x100, 0x0FF,
    0x0FF, 0x000, 0x12C, 0x02C, 0x02C, 0x02C, 0x02C, 0x000,
    0x032, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
    0x038, 0x00B, 0x032, 0x000, 0x008, 0x00C, 0x093, 0x0E9,
    0x000, 0x000, 0x000, 0x000, 0x003, 0x010, 0x010, 0x100,
    0x100, 0x002, 0x001, 0x001, 0x039, 0x039, 0x039, 0x039,
    0x001, 0x001
};

////////////////////////////////////////////////////////////////////////////////
/// @brief  Configure I2C GPIO and check whether WM8978 on the I2C bus is normal
/// @param  None
/// @retval re: 1 : normal, 0 : abnormal
////////////////////////////////////////////////////////////////////////////////
uint8_t wm8978_Init(void)
{
    uint8_t re;

    if (i2c_CheckDevice(WM8978_SLAVE_ADDRESS) == 0) { // This function will configure the GPIO of MM32 for software to simulate I2C timing
        re = 1;
    }
    else {
        re = 0;
    }
    wm8978_Reset();         // Hardware reset all registers of WM8978 to default state
    wm8978_CtrlGPIO1(1);    // WM8978's GPIO1 pin outputs 1, which means that the default is to play sound (only for Avnet development board hardware needs)

    return re;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Initialize wm8978 hardware device, DAC output to earphone
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_Dac2Ear(void)
{
    wm8978_CfgInOut(DAC_ON, AUX_OFF, LINE_OFF, SPK_OFF, EAR_ON);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Initialize wm8978 hardware device, DAC output to speaker
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_Dac2Spk(void)
{
    wm8978_CfgInOut(DAC_ON, AUX_OFF, LINE_OFF, SPK_ON, EAR_OFF);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Initialize the wm8978 hardware device, Aux (FM radio) output to the headset
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_Aux2Ear(void)
{
    wm8978_CfgInOut(DAC_OFF, AUX_ON, LINE_OFF, SPK_OFF, EAR_ON);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Initialize the wm8978 hardware device,
///         Aux (FM radio) output to the headset
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_Aux2Spk(void)
{
    wm8978_CfgInOut(DAC_OFF, AUX_ON, LINE_OFF, SPK_ON, EAR_OFF);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief Configure wm8978 hardware, input mixing
///       (AUX, LINE, MIC) directly output to headphones
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_Mic2Ear(void)
{
    wm8978_CfgInOut(DAC_OFF, AUX_OFF, LINE_ON, SPK_OFF, EAR_ON);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Input mixing (AUX, LINE, MIC) directly output to the speaker
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_Mic2Spk(void)
{
    wm8978_CfgInOut(DAC_OFF, AUX_OFF, LINE_ON, SPK_ON, EAR_OFF);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  MIC input to ADC, ready to record
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_Mic2Adc(void)
{
    wm8978_CfgAdc(MIC_ON, AUX_OFF, LINE_ON);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Modify output volume
/// @param  _ucLeftVolume: Left channel volume value
/// @param  _ucLRightVolume: Right channel volume value
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_ChangeVolume(uint8_t _ucLeftVolume, uint8_t _ucRightVolume)
{
    uint16_t regL;
    uint16_t regR;

    if (_ucLeftVolume > 0x3F) {
        _ucLeftVolume = 0x3F;
    }

    if (_ucRightVolume > 0x3F) {
        _ucRightVolume = 0x3F;
    }

    regL = _ucLeftVolume;
    regR = _ucRightVolume;


    wm8978_WriteReg(52, regL | 0x00);


    wm8978_WriteReg(53, regR | 0x100);


    wm8978_WriteReg(54, regL | 0x00);


    wm8978_WriteReg(55, regR | 0x100);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Read back the volume of the channel.
/// @param  None
/// @retval Current volume value
////////////////////////////////////////////////////////////////////////////////
uint8_t wm8978_ReadVolume(void)
{
    return (uint8_t)(wm8978_ReadReg(52) & 0x3F );
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  The output is muted.
/// @param  _ucMute: 1 is mute, 0 is not mute.
/// @retval Current volume value
////////////////////////////////////////////////////////////////////////////////
void wm8978_Mute(uint8_t _ucMute)
{
    uint16_t usRegValue;

    if (_ucMute == 1) {
        usRegValue = wm8978_ReadReg(52);
        usRegValue |= (1u << 6);
        wm8978_WriteReg(52, usRegValue);

        usRegValue = wm8978_ReadReg(53);
        usRegValue |= (1u << 6);
        wm8978_WriteReg(53, usRegValue);

        usRegValue = wm8978_ReadReg(54);
        usRegValue |= (1u << 6);
        wm8978_WriteReg(54, usRegValue);

        usRegValue = wm8978_ReadReg(55);
        usRegValue |= (1u << 6);
        wm8978_WriteReg(55, usRegValue);
    }
    else {
        usRegValue = wm8978_ReadReg(52);
        usRegValue &= ~(1u << 6);
        wm8978_WriteReg(52, usRegValue);

        usRegValue = wm8978_ReadReg(53);
        usRegValue &= ~(1u << 6);
        wm8978_WriteReg(53, usRegValue);

        usRegValue = wm8978_ReadReg(54);
        usRegValue &= ~(1u << 6);
        wm8978_WriteReg(54, usRegValue);

        usRegValue = wm8978_ReadReg(55);
        usRegValue &= ~(1u << 6);
        wm8978_WriteReg(55, usRegValue);
    }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Set MIC gain
/// @param  _ucGain: volume value
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_SetMicGain(uint8_t _ucGain)
{
    if (_ucGain > GAIN_MAX) {
        _ucGain = GAIN_MAX;
    }

    wm8978_WriteReg(45, _ucGain);
    wm8978_WriteReg(46, _ucGain | (1 << 8));
}

////////////////////////////////////////////////////////////////////////////////
/// @brief   Turn off wm8978 and enter low power mode
/// @param   None
/// @retval  None
////////////////////////////////////////////////////////////////////////////////
void wm8978_PowerDown(void)
{

    uint16_t usRegValue;

    usRegValue = wm8978_ReadReg(10);
    usRegValue |= (1u << 6);
    wm8978_WriteReg(10, usRegValue);


}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Configure the audio interface (I2S) of WM8978
/// @param  _usStandard:Interface standard, I2S_Standard_Phillips,
///                     I2S_Standard_MSB or I2S_Standard_LSB
/// @param  _ucWordLen:word length, 16, 24, 32
///                    (discard the less commonly used 20bit format)
/// @param  _usMode:  CPU I2S working mode, I2S_Mode_MasterTx, I2S_Mode_MasterRx,
/// @param            Avnet development board hardware does not support
///                   I2S_Mode_SlaveTx, I2S_Mode_SlaveRx mode, which requires
///                   WM8978 connection External oscillator
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void wm8978_CfgAudioIF(uint16_t _usStandard, uint8_t _ucWordLen, uint16_t _usMode)
{
    uint16_t usReg;


    usReg = 0;
    if (_usStandard == I2S_Standard_Phillips) {
        usReg |= (2 << 3);
    }
    else if (_usStandard == I2S_Standard_MSB) {
        usReg |= (1 << 3);
    }
    else if (_usStandard == I2S_Standard_LSB) {
        usReg |= (0 << 3);
    }
    else {
        usReg |= (3 << 3);;
    }

    if (_ucWordLen == 24) {
        usReg |= (2 << 5);
    }
    else if (_ucWordLen == 32) {
        usReg |= (3 << 5);
    }
    else {
        usReg |= (0 << 5);
    }
    wm8978_WriteReg(4, usReg);


    wm8978_WriteReg(6, 0x000);


    if (_usMode == SPI_Mode_Master) {
        wm8978_CtrlGPIO1(1);
    }
    else {
        wm8978_CtrlGPIO1(0);
    }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Read back from cash and read back wm8978 register
/// @param  _ucRegAddr: Register address
/// @retval None
////////////////////////////////////////////////////////////////////////////////
static uint16_t wm8978_ReadReg(uint8_t _ucRegAddr)
{
    return wm8978_RegCash[_ucRegAddr];
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Write wm8978 register
/// @param  _ucRegAddr: Register address
/// @param  _usValue : Register value
/// @retval None
////////////////////////////////////////////////////////////////////////////////
static uint8_t wm8978_WriteReg(uint8_t _ucRegAddr, uint16_t _usValue)
{
    uint8_t ucAck;


    i2c_Start();


    i2c_SendByte(WM8978_SLAVE_ADDRESS | I2C_WR);


    ucAck = i2c_WaitAck();
    if (ucAck == 1) {
        return 0;
    }


    i2c_SendByte(((_ucRegAddr << 1) & 0xFE) | ((_usValue >> 8) & 0x1));


    ucAck = i2c_WaitAck();
    if (ucAck == 1) {
        return 0;
    }


    i2c_SendByte(_usValue & 0xFF);


    ucAck = i2c_WaitAck();
    if (ucAck == 1) {
        return 0;
    }


    i2c_Stop();

    wm8978_RegCash[_ucRegAddr] = _usValue;
    return 1;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Configure wm8978 input and output channels
/// @param  _ucDacEn : DAC input channel enable (digital audio signal
///                    transmitted by the CPU through the I2S interface)
/// @param  _ucAuxEn : Auxiliary input channel enable (audio output signal of
///                    FM radio module)
/// @param  _ucLineEn: Line input channel enable (V2 board is empty,V3 board
///                    is connected to the audio output of VS1003B decoder chip)
/// @param  _ucSpkEn: speaker output enable
/// @param  _ucEarEn: Headphone output enable
/// @retval Noone
////////////////////////////////////////////////////////////////////////////////
static void wm8978_CfgInOut(uint8_t _ucDacEn, uint8_t _ucAuxEn, uint8_t _ucLineEn, uint8_t _ucSpkEn, uint8_t _ucEarEn)
{
    uint16_t usReg;


    usReg = 0;
    if ((_ucSpkEn == 1) || (_ucEarEn == 1)) {
        usReg = ((1 << 3) | (3 << 0));
    }
    if (_ucEarEn == 1) {
        usReg |= (1 << 6);
    }
    wm8978_WriteReg(1, usReg);


    usReg = 0;
    if (_ucEarEn == 1) {
        usReg = ((1 << 8) | (1 << 7));
    }
    wm8978_WriteReg(2, usReg);


    usReg = 0;
    if ((_ucSpkEn == 1) || (_ucEarEn == 1)) {
        usReg |= ((1 << 3) | (1 << 2));
    }
    if (_ucEarEn == 1) {
        usReg |= (1 << 7);
    }
    if (_ucSpkEn == 1) {
        usReg |= ((1 << 6) | (1 << 5));
    }
    if (_ucDacEn == 1) {
        usReg |= ((1 << 1) | (1 << 0));
    }
    wm8978_WriteReg(3, usReg);


    if (_ucDacEn == 1) {
#if 0
        wm8978_WriteReg(11, 255);
        wm8978_WriteReg(12, 255 | 0x100);
#endif
    }
    else {
        ;
    }

    usReg = 0;
    if (_ucSpkEn == 1) {
        usReg |= (1 << 4);
    }
    if (_ucAuxEn == 1) {
        usReg |= ((7 << 1) | (1 << 0));
    }
    wm8978_WriteReg(43, usReg);


    usReg = 0;
    if (_ucDacEn == 1) {
        usReg |= ((0 << 6) | (0 << 5));
    }
    if (_ucSpkEn == 1) {
        usReg |=  ((0 << 2) | (1 << 1));
    }
    wm8978_WriteReg(49, usReg);


    usReg = 0;
    if (_ucAuxEn == 1) {
        usReg |= ((7 << 6) | (1 << 5));
    }
    if (_ucLineEn == 1) {
        usReg |= ((7 << 2) | (1 << 1));
    }
    if (_ucDacEn == 1) {
        usReg |= (1 << 0);
    }
    wm8978_WriteReg(50, usReg);
    wm8978_WriteReg(51, usReg);

#if 0
    if (_ucEarEn == 1) {
        usReg = (0x3f | (1 << 7));
        wm8978_WriteReg(52, usReg);
        wm8978_WriteReg(53, usReg | (1 << 8));
    }
    else {
        usReg = ((1 << 7) | (1 << 6));
        wm8978_WriteReg(52, usReg);
        wm8978_WriteReg(53, usReg | (1 << 8));
    }

    if (_ucSpkEn == 1) {
        usReg = (0x3f | (1 << 7));
        wm8978_WriteReg(54, usReg);
        wm8978_WriteReg(55, usReg | (1 << 8));
    }
    else {
        usReg = ((1 << 7) | (1 << 6));
        wm8978_WriteReg(54, usReg);
        wm8978_WriteReg(55, usReg | (1 << 8));
    }
#endif


    wm8978_WriteReg(56, (1 << 6));


    if (_ucDacEn == 1) {
        wm8978_WriteReg(10, 0);
    }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief Configure wm8978 ADC channel
/// @param  _ucMicEn: MIC input channel enable (V2 and V3 onboard MIC are
///                   connected to the left channel)
/// @param  _ucAuxEn: Auxiliary input channel enable (audio output signal
///                   of FM radio module)
/// @param  _ucLineEn: Line input channel enable (V2 board is empty, V3 board
///                   is connected to the audio output of VS1003B decoder chip)
/// @retval None
////////////////////////////////////////////////////////////////////////////////
static void wm8978_CfgAdc(uint8_t _ucMicEn, uint8_t _ucAuxEn, uint8_t _ucLineEn)
{
    uint16_t usReg;


    if (_ucMicEn == 1) {

        usReg = wm8978_ReadReg(1);
        usReg |= (1 << 4);
        wm8978_WriteReg(1, usReg);


        usReg = wm8978_ReadReg(44);
        usReg |= (0 << 4);
        usReg |= ((1 << 1) | (1 << 0));
        wm8978_WriteReg(44, usReg);
    }


    if ((_ucMicEn == 0) && (_ucAuxEn == 0) && (_ucLineEn == 0)) {
        usReg = 0;
    }
    else {
        usReg = (1 << 8) | (1 << 7) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0);
    }
    wm8978_WriteReg(2, usReg);


    usReg = 0;
    if (_ucMicEn == 1) {
        usReg |= (1 << 8);
    }
    if (_ucAuxEn == 1) {
        usReg |= (3 << 4);
    }
    if (_ucLineEn == 1) {
        usReg |= (3 << 0);
    }
    wm8978_WriteReg(47, usReg);
    wm8978_WriteReg(48, usReg);


    usReg = (1 << 3);
    wm8978_WriteReg(14, usReg);



    usReg = (0 << 7);
    wm8978_WriteReg(27, usReg);
    usReg = 0;
    wm8978_WriteReg(28, usReg);
    wm8978_WriteReg(29, usReg);
    wm8978_WriteReg(30, usReg);


    usReg = 0xFF;
    wm8978_WriteReg(15, usReg);
    usReg = 0x1FF;
    wm8978_WriteReg(16, usReg);

#if 0
    usReg = (16 << 0);
    wm8978_WriteReg(45, usReg);
    wm8978_WriteReg(46, usReg | (1 << 8));
#endif


    usReg = wm8978_ReadReg(32);
    usReg &= ~((1 << 8) | (1 << 7));
    wm8978_WriteReg(32, usReg);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Control WM8978's GPIO1 pin to output 0 or 1
/// @param  _ucValue: GPIO1 output value, 0 or 1
/// @retval Current volume value
////////////////////////////////////////////////////////////////////////////////
static void wm8978_CtrlGPIO1(uint8_t _ucValue)
{
    uint16_t usRegValue;


    if (_ucValue == 0) {
        usRegValue = 6;
    }
    else {
        usRegValue = 7;
    }
    wm8978_WriteReg(8, usRegValue);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Reset wm8978, all register values are restored to default values
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
static void wm8978_Reset(void)
{

    const uint16_t reg_default[] = {
        0x000, 0x000, 0x000, 0x000, 0x050, 0x000, 0x140, 0x000,
        0x000, 0x000, 0x000, 0x0FF, 0x0FF, 0x000, 0x100, 0x0FF,
        0x0FF, 0x000, 0x12C, 0x02C, 0x02C, 0x02C, 0x02C, 0x000,
        0x032, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
        0x038, 0x00B, 0x032, 0x000, 0x008, 0x00C, 0x093, 0x0E9,
        0x000, 0x000, 0x000, 0x000, 0x003, 0x010, 0x010, 0x100,
        0x100, 0x002, 0x001, 0x001, 0x039, 0x039, 0x039, 0x039,
        0x001, 0x001
    };
    uint8_t i;

    wm8978_WriteReg(0x00, 0);

    for (i = 0; i < sizeof(reg_default) / 2; i++) {
        wm8978_WriteReg(i, reg_default[i]);
        wm8978_RegCash[i] = reg_default[i];

    }
}


////////////////////////////////////////////////////////////////////////////////
/// @brief  Configure GPIO pins and interrupt channels for codec applications
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void I2S_CODEC_Init(void)
{

    I2S_GPIO_Config();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Configure the I2S working mode, start the I2S send buffer empty
///         interrupt, and start playing
/// @param  _usStandard: Interface standard, I2S_Standard_Phillips,
///         I2S_Standard_MSB or I2S_Standard_LSB
/// @param  _usMCLKOutput: main clock output, I2S_MCLKOutput_Enable or
///         I2S_MCLKOutput_Disable
/// @param  _usAudioFreq: sampling frequency, I2S_AudioFreq_8K,
///         I2S_AudioFreq_16K, I2S_AudioFreq_22K,
///         I2S_AudioFreq_44K, I2S_AudioFreq_48
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void I2S_StartPlay(SPI_I2S_STANDARD_TypeDef _usStandard, SPI_I2S_DATAFORMAT_TypeDef _usWordLen, SPI_I2S_AUDIO_FREQ_TypeDef _usAudioFreq)
{

    I2S_Mode_Config((SPI_I2S_STANDARD_TypeDef)_usStandard, (SPI_I2S_DATAFORMAT_TypeDef)_usWordLen, (SPI_I2S_AUDIO_FREQ_TypeDef)_usAudioFreq, (SPI_I2S_TRANS_MODE_TypeDef)I2S_Mode_MasterTx);

}
////////////////////////////////////////////////////////////////////////////////
/// @brief  DMA initialize
/// @param  bufo:DMA_MemoryBaseAddr
/// @param  num : BufferSize
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void I2S_TX_DMAPolling_Circular_Auto_Reload_Enable_Init(u16* buf0, u16 num)
{

    DMA_InitTypeDef  DMA_InitStructure;


    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel3);

    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32) & (SPI1->TXREG);
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)buf0;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = num;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_InitStructure.DMA_Auto_reload = DMA_Auto_Reload_Enable;
    DMA_Init(DMA1_Channel3, &DMA_InitStructure);


}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Stop I2S work
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
void I2S_Stop(void)
{

    SPI_ITConfig(SPI1, SPI_IT_TX, DISABLE);


    SPI_ITConfig(SPI1, SPI_IT_RX, DISABLE);

    I2S_Cmd(SPI1, DISABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE);
}


////////////////////////////////////////////////////////////////////////////////
/// @brief  Configure GPIO pins for codec applications
/// @param  None
/// @retval None
////////////////////////////////////////////////////////////////////////////////
static void I2S_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // Enable GPIOB, GPIOC and AFIO clock
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOC, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5);
    // I2S1 SD, CK and WS pins configuration
#if 1
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

#else

    GPIO_InitStructure.GPIO_Pin =   GPIO_Pin_5 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // I2S1 MCK pin configuration
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
#endif
}


////////////////////////////////////////////////////////////////////////////////
/// @brief  Configure MM32's I2S peripheral working mode
/// @param  _usStandard: Interface standard, I2S_Standard_Phillips,
///                      I2S_Standard_MSB or I2S_Standard_LSB
/// @param  _usMCLKOutput: main clock output, I2S_MCLKOutput_Enable or
///                      I2S_MCLKOutput_Disable
/// @param  _usAudioFreq: sampling frequency, I2S_AudioFreq_8K, I2S_AudioFreq_16K,
///                      I2S_AudioFreq_22K,I2S_AudioFreq_44K, I2S_AudioFreq_48
/// @param    _usMode:CPU I2S working mode, I2S_Mode_MasterTx, I2S_Mode_MasterRx,
///                   Avnet development board hardware does not support
///                   I2S_Mode_SlaveTx, I2S_Mode_SlaveRx mode, which requires
///                   WM8978 connection External oscillator
/// @retval  None
////////////////////////////////////////////////////////////////////////////////
static void I2S_Mode_Config(SPI_I2S_STANDARD_TypeDef _usStandard, SPI_I2S_DATAFORMAT_TypeDef _usWordLen, SPI_I2S_AUDIO_FREQ_TypeDef _usAudioFreq, SPI_I2S_TRANS_MODE_TypeDef _usMode)
{
    I2S_InitTypeDef I2S_InitStructure;

    if ((_usMode == I2S_Mode_SlaveTx) && (_usMode == I2S_Mode_SlaveRx)) {

        return;
    }

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    SPI_DeInit(SPI1);
    I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;
    I2S_InitStructure.I2S_Standard = _usStandard;
    I2S_InitStructure.I2S_DataFormat = _usWordLen;
    I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;
    I2S_InitStructure.I2S_AudioFreq = _usAudioFreq;
    I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
    I2S_Init(SPI1, &I2S_InitStructure);

    SPI1->RXDNR = 0xFFFF;
    SPI_DMACmd(SPI1, ENABLE);
    I2S_Cmd(SPI1, ENABLE);
    SPI1->GCTL |= 0xF;
}
/// @}

/// @}

/// @}

